package ccm.communicate.tcp;

import ccm.common.Configurable;
import ccm.common.ReceiveDataEvent;
import ccm.common.StatusChangeEvent;
import ccm.communicate.common.Communicate;
import ccm.communicate.common.NotStartException;
import ccm.communicate.common.SendFailedException;

import javax.swing.*;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;

public class TcpClient extends Communicate implements Configurable
{
    /**
     * 保存链接
     */
    protected Socket       clientSocket;
    /**
     * 写入数据流
     */
    protected OutputStream outToServer;
    /**
     * 读取数据流
     */
    protected InputStream  inFromServer;
    /**
     * 保存读取线程
     */
    protected Receiver     receiver;
    /**
     * 保存主机地址
     */
    protected String       host="192.168.2.7";
    /**
     * 保存端口号
     */
    protected int          port=3333;

    public TcpClient()
    {
        super();
        throw new RuntimeException();
    }

    /**
     * 获取配置用的面板
     *
     * @return 配置用的面板
     */
    public JPanel getConfigJPanel()
    {
        return new TcpClientConfigJPanel(this);
    }

    /**
     * 启动
     */
    public void start()
    {
        try
        {
            clientSocket=new Socket(host,port);
            outToServer =clientSocket.getOutputStream();
            inFromServer=clientSocket.getInputStream();
            (receiver=new Receiver()).start();
            statusChange(StatusChangeEvent.START_STOP);
        }catch(IOException e)
        {
            e.printStackTrace();
            clientSocket=null;
            outToServer =null;
            inFromServer=null;
        }
    }

    /**
     * 停止
     */
    public void stop()
    {
        try
        {
            if(clientSocket!=null)
                clientSocket.close();
        }catch(IOException e)
        {
            e.printStackTrace();
        }
        clientSocket=null;
        outToServer =null;
        inFromServer=null;
        receiver    =null;
        statusChange(StatusChangeEvent.START_STOP);
    }

    /**
     * 检查是否开启
     *
     * @return true表示开启
     * false表示关闭
     */
    public boolean isOpen()
    {
        return (clientSocket!=null&&outToServer!=null&&inFromServer!=null&&!clientSocket.isClosed());
    }

    /**
     * 发送数据
     *
     * @param data    数据
     * @param channel 通道
     * @throws NotStartException   未启动时抛出此异常
     * @throws SendFailedException 发送失败时抛出此异常
     * @implNote 通道未使用
     */
    public void send(byte[] data,int channel) throws NotStartException, SendFailedException
    {
        if(!isOpen())
            throw new NotStartException();
        try
        {
            outToServer.write(data);
        }catch(IOException e)
        {
            e.printStackTrace();
            throw new SendFailedException();
        }
    }

    /**
     * 用于保存参数
     */
    public void writeExternal(ObjectOutput out) throws IOException
    {
        out.writeObject(getHost());
        out.writeInt(getPort());

    }

    /**
     * 获取主机地址
     *
     * @return 主机地址
     */
    public String getHost()
    {
        return host;
    }

    /**
     * 设置主机地址
     *
     * @param host 主机地址
     */
    public void setHost(String host)
    {
        this.host=host;
        statusChange(StatusChangeEvent.CONFIG);
        updateSetting();
    }

    /**
     * 获取主机端口
     *
     * @return 主机端口
     */
    public int getPort()
    {
        return port;
    }

    /**
     * 设置主机端口
     *
     * @param port 端口
     */
    public void setPort(int port)
    {
        this.port=port;
        statusChange(StatusChangeEvent.CONFIG);
        updateSetting();
    }

    /**
     * 用于加载参数
     */
    public void readExternal(ObjectInput in) throws IOException
    {
        try
        {
            setHost((String)in.readObject());
        }catch(ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        setPort(in.readInt());
    }

    /**
     * 更新设置
     */
    protected void updateSetting()
    {
        if(isOpen())
        {
            stop();
            start();
        }
    }

    private class Receiver extends Thread
    {
        public void run()
        {
            try
            {
                while(isOpen())
                {
                    byte[] buffer=new byte[1024];
                    int len;
                    if((len=inFromServer.read(buffer))!=-1)
                    {
                        byte[] buf=new byte[len];
                        System.arraycopy(buffer,0,buf,0,len);
                        ReceiveDataEvent e=new ReceiveDataEvent(this,buf);
                        for(int i=0;i<receiveDataListener.length;++i)
                            callReceiveDataListener(i,e);
                    }
                }
            }catch(SocketException e)
            {
                TcpClient.this.stop();
                e.printStackTrace();
            }catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }
}
